﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace QuMianChaZhi.Contour
{
    public class ContourMethods
    {
        public List<Triangle> RawTriangles { get; set; }

        public Dictionary<string,VPoint> ExistedPoints { get; set; }

        public Dictionary<string,Edge> ExistedEdge { get; set; }


        double Tolerance = 0.000001;


        public VPoint FindOrCreateNewPoint(int x,int y, double v)
        {
            string key = x + "-" + y;

            if (ExistedPoints.ContainsKey(key))
            {
                return ExistedPoints[key];
            }

            VPoint vp = new VPoint()
            {
                X = x,
                Y = y,
                Value = v
            };

            ExistedPoints.Add(key, vp);
            return vp;
        }

        public Edge FindOrCreateNewEdge(int x1,int y1,double v1, int x2,int y2, double v2)
        {
            string key = x1 + "-" + y1 + "_" + x2 + "-" + y2;
            if(ExistedEdge.ContainsKey(key))
            {
                return ExistedEdge[key];
            }

            VPoint p1 = FindOrCreateNewPoint(x1, y1, v1);
            VPoint p2 = FindOrCreateNewPoint(x2, y2, v2);

            Edge edge = new Edge()
            {
                P1 = p1,
                P2 = p2,

                Key = key
            };

            ExistedEdge.Add(key, edge);

            return edge;
        }

        public List<ContourSurface> GenerateTrianges(ReadInTemp rawdata)
        {
            int step = 20;

            ExistedPoints = new Dictionary<string, VPoint>();
            ExistedEdge = new Dictionary<string, Edge>();
            RawTriangles = new List<Triangle>();

            for (int x = 0; x < rawdata.W - step; x+= step)
            {
                for(int y = 0; y < rawdata.H - step; y += step)
                {

                    var p1 = rawdata.RawData[x][y];
                    var p2 = rawdata.RawData[x + step][y];
                    var p3 = rawdata.RawData[x + step][y + step];
                    var p4 = rawdata.RawData[x][y + step];


                    var edge1 = this.FindOrCreateNewEdge(x, y, p1, x + step, y, p2);
                    var edge2 = this.FindOrCreateNewEdge(x + step, y, p2, x + step, y + step, p3);
                    var edge3 = this.FindOrCreateNewEdge(x, y + step, p4, x + step, y + step, p3);
                    var edge4 = this.FindOrCreateNewEdge(x, y, p1, x, y + step, p4);

                    var edge5 = this.FindOrCreateNewEdge(x, y, p1, x + step, y + step, p3);

                    Triangle triangle1 = new Triangle();
                    Triangle triangle2 = new Triangle();

                    triangle1.A = edge1;
                    triangle1.B = edge2;
                    triangle1.C = edge5;

                    triangle2.A = edge3;
                    triangle2.B = edge4;
                    triangle2.C = edge5;


                    RawTriangles.Add(triangle1);
                    RawTriangles.Add(triangle2);

                }
            }

           
           return FindAllPossibleTriangles(rawdata);

            Console.WriteLine("Finished");
        }


        private List<ContourSurface> FindAllPossibleTriangles(ReadInTemp rawdata)
        {
          

            List<ContourSurface> surfaces = new List<ContourSurface>();

            //double max = rawdata.Max;
            //double min = rawdata.Min;

            //double offset = (max - min) / 100;

            //for (double start = min + offset; start < max; start += offset)
            //{
            //    ContourSurface faceOne = FindAllPossibleContourLines(start);

            //    if (faceOne != null)
            //    {
            //        surfaces.Add(faceOne);
            //    }
            //}

            int start = -100;

            foreach(var item in rawdata.ValueCount)
            {
                if(item.Value < 4)
                {
                    continue;
                }

                if(item.Key < start)
                {
                    continue;
                }

                start = item.Key + 3;

                ContourSurface faceOne = FindAllPossibleContourLines(item.Key);

                if (faceOne != null)
                {
                    surfaces.Add(faceOne);
                }
            }

            return surfaces;

            Console.WriteLine("Finished");
        }


        private ContourSurface FindAllPossibleContourLines(double vl)
        {

            List<Triangle> possibleTriangle = new List<Triangle>();

            foreach (var ag in RawTriangles)
            {
                var edges = ag.GetEdges();

                var count = 0;
                foreach (var edge in edges)
                {
                    if (IsEdgeIncludeExpectedValue(edge, vl))
                    {
                        count++;
                    }
                }

                if (count >= 2)
                {
                    ag.UsedStatus = 0;
                    possibleTriangle.Add(ag);
                }
            }


            List<ContourLine> lines = new List<ContourLine>();

            foreach (var triangle in possibleTriangle)
            {
                if (triangle.UsedStatus != 0)
                {
                    continue;
                }
                var edges = triangle.GetEdges();

                var count = 0;
                foreach (var edge in edges)
                {
                    if (IsEdgeIncludeExpectedValue(edge, vl))
                    {

                        triangle.UsedStatus = 1;

                        var points = GetContour(edge, vl);

                        if (points != null)
                        {

                            ContourLine cl = new ContourLine();
                            cl.Value = vl;
                            cl.Points = points;

                            lines.Add(cl);
                        }

                        break;
                    }
                }
            }

            if(lines.Count == 0)
            {
                return null;
            }

            ContourSurface surface = new ContourSurface();
            surface.Value = vl;
            surface.Lines = lines;

            return surface;
        }




        private List<VPoint> GetContour(Edge currentEdge,double value)
        {
            List<VPoint> result = new List<VPoint>();

            var firstEdge = currentEdge;
            var currentTrangle = currentEdge.Triangle1;
            var currentPoint = this.TryGetContourPoints(currentEdge, value);
            result.Add(currentPoint);

            List<Triangle> trianglesTemp = new List<Triangle>();

            while (true)
            {
                currentTrangle.UsedStatus = -1;

                trianglesTemp.Add(currentTrangle);

                var edges = currentTrangle.Get2OtherEdge(currentEdge);
                currentPoint = this.TryGetContourPoints(edges[0], value);

                int useEdge = 0;

                if (currentPoint == null)
                {
                    currentPoint = this.TryGetContourPoints(edges[1], value);
                    useEdge = 1;
                }

                result.Add(currentPoint);
                currentEdge = edges[useEdge];
                currentTrangle = currentTrangle == currentEdge.Triangle1 ? currentEdge.Triangle2 : currentEdge.Triangle1;

                if (currentTrangle == null || currentTrangle.UsedStatus == -1 || currentTrangle.UsedStatus == 1)
                {
                    int usedStatus = 0;

                    //开放式等值线 
                    if ((firstEdge.IsBorder && currentEdge.IsBorder && result.Count >= 2) ||
                        //封闭式等值线
                        (firstEdge.IsBorder == false && currentTrangle != null && currentTrangle.UsedStatus == -1 && result.Count >= 6 && currentEdge == firstEdge))
                    {
                        //usedStatus = 1;
                        //this.CompleteContour(triangles, usedStatus);

                        foreach(var tmpTriangle in trianglesTemp)
                        {
                            tmpTriangle.UsedStatus = 1;
                        }

                        //currentTrangle.UsedStatus = 1;
                    }
                    else
                    {
                        //this.CompleteContour(triangles, usedStatus);
                        // currentTrangle.UsedStatus = 0;

                        foreach (var tmpTriangle in trianglesTemp)
                        {
                            tmpTriangle.UsedStatus = 0;
                        }

                        result = null;

                    }

                    break;
                }
            }

            return result;

        }

        private bool IsEdgeIncludeExpectedValue(Edge eg,double vl)
        {
            double value1 = eg.P1.Value;
            double value2 = eg.P2.Value;

            //if(value1 == vl)
            //{
            //    value1 -= Tolerance;
            //}

            //if(value2 == vl)
            //{
            //    value2 -= Tolerance;
            //}

            return (value1 - vl) * (value2 - vl) <= 0;
        }

        private VPoint TryGetContourPoints(Edge edge, double value)
        {
            if (!IsEdgeIncludeExpectedValue(edge, value))
            {
                return null;
            }

            VPoint result = new VPoint();
            if (edge.P2.Value == edge.P1.Value)
            {
                result.X = (edge.P1.X + edge.P2.X) / 2;
                result.Y = (edge.P1.Y + edge.P2.Y) / 2;
            }
            else
            {
                var factor = (float)((value - edge.P1.Value) / (edge.P2.Value - edge.P1.Value));
                result.X = edge.P1.X + (int)(factor * (edge.P2.X - edge.P1.X));
                result.Y = edge.P1.Y + (int)(factor * (edge.P2.Y - edge.P1.Y));
            }

            result.Value = value;

            return result;
        }
    }
}
